/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
export var fourSum = function (nums, target) {
  if (nums.length < 4) return []
  nums.sort((a, b) => a - b)
  const ans = []
  const n = nums.length
  for (let a = 0; a < n - 3; a++) {
    if (a > 0 && nums[a] === nums[a - 1]) continue
    if (nums[a] + nums[a + 1] + nums[a + 2] + nums[a + 3] > target) break
    if (nums[a] + nums[n - 3] + nums[n - 2] + nums[n - 1] < target) continue
    const diff = target - nums[a]
    const threeRes = threeSum(nums, diff, a + 1)
    if (threeRes.length > 0) {
      ans.push(...threeRes.map(res => [nums[a], ...res]))
    }
  }
  return ans
}

/**
 * 三数之和
 * @param {number[]} nums
 * @return {number[][]}
 */
function threeSum(nums, target, startIdx = 1) {
  const ans = []
  const n = nums.length
  // a, b, c三个指针
  for (let a = startIdx; a < nums.length - 2; a++) {
    // 如果 nums[a] 和一个数相同。跳过以避免重复
    if (a > startIdx && nums[a] === nums[a - 1]) continue
    if (nums[a] + nums[a + 1] + nums[a + 2] > target) break
    if (nums[a] + nums[n - 2] + nums[n - 1] < target) continue
    let b = a + 1
    let c = nums.length - 1
    while (b < c) {
      // 找到了一个结果
      const sum = nums[a] + nums[b] + nums[c]
      if (sum === target) {
        ans.push([nums[a], nums[b], nums[c]])
        // 找到一个后须要继续找下一个
        b++
        while (b < c && nums[b] === nums[b - 1]) b++
        c--
        while (b < c && nums[c] === nums[c + 1]) c--
      } else if (sum > target) {
        c--
      } else {
        b++
      }
    }
  }
  return ans
}
